/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2011 SMUNIX
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
* Author: Providence M. Salumu <providence.salumu@smunix.com>
*/


#ifndef AGGREGATOR_HPP__191208
#define AGGREGATOR_HPP__191208

#include "details/aggregator.hpp"
#include "injectable.hpp"

namespace smunix
{
  template<class T>
  class Aggregated
  {
    typedef std::shared_ptr<T> SP;
    typedef Injector<T> InjectorType;
  public:
    typedef Aggregated ThisType;
    template<class XY>
    struct Rebind { typedef Aggregated<XY> Type; };
  public:
    virtual SP const & operator() () const
    {
      // TODO : Check that mClient is initialized!
      return mClient;
    }
    virtual SP & operator() ()
    {
      // TODO : Check that mClient is initialized!
      return mClient;
    }
  protected:
    Aggregated (void)
    {
      InjectorType::Instance ().Add (this);
    }
    virtual ~Aggregated()
    {
      InjectorType::Instance ().Clean (this);
    }
  private:
    // Friends!
    template<class> 
    friend class AggregatedPeeker;
    template<class Derived, class ...Args>
    friend class Aggregator;

    SP mClient;
  }; // class Aggregated

  /**
   * Injector Dispatch () method.
   */
  template<class T, class IsInjectableOrNot>
  void Injector<T, IsInjectableOrNot>::Dispatch (SP i)
  {
    Lock lock (mMutex);
    for (auto &r : mObjects)
      (*r) () = i;
    mObjects.clear ();
  }

  template<class Derived>
  class AggregatedPeeker
  {
  public:
    typedef AggregatedPeeker ThisType;
    template<class XY>
    struct Rebind { typedef AggregatedPeeker<XY> Type; };
  public:
    virtual ~AggregatedPeeker()
    {}
    template<class I>
    std::shared_ptr<I> Peek (void) const
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    I const & Ref (void) const
    {
      return *Peek<I> ();
    }
    template<class I>
    I & Ref (void)
    {
      return *Peek<I> ();
    }
    template<class I, class K>
    void Pack (K k)
    {
      details::SwapHelper<I>::Reset (k) (GetAggregated<I> ().mClient);
    }
  protected:
    AggregatedPeeker()
    {}
  private:
    template<class I>
    Aggregated<I> & GetAggregated (void)
    {
      return GetDerived ();
    }
    template<class I>
    Aggregated<I> const & GetAggregated (void) const
    {
      return GetDerived ();
    }
    Derived & GetDerived (void)
    {
      return static_cast<Derived &> (*this);
    }
    Derived const & GetDerived (void) const
    {
      return static_cast<Derived const &> (*this);
    }
  }; // class Peeker
} // namespace smunix

namespace smunix
{
  template<class Derived, class ...Args>
  class Aggregator;
} // namespace smunix

namespace smunix
{
  template<class Derived, class InitialArg, class ...Args>
  class Aggregator<Derived, InitialArg, Args...> : virtual protected details::AggregatorBase, virtual public Aggregated<InitialArg>, public Aggregator<Derived, Args...>
  {
  public:
    typedef Aggregator ThisType;
    template<class XY1, class XY2, class ...XY3>
    struct Rebind { typedef Aggregator<XY1, XY2, XY3...> Type; };
  public:
    virtual ~Aggregator()
    {}
    template<class I>
    std::shared_ptr<I> const & PeekWithRef (void) const
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    std::shared_ptr<I> & PeekWithRef (void)
    {
      auto &ret = GetAggregated<I> () ();
      return  ret;
    }
    template<class I>
    std::shared_ptr<I> const Peek (void) const
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    std::shared_ptr<I> Peek (void)
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    I const & Ref (void) const
    {
      return *Peek<I> ();
    }
    template<class I>
    I & Ref (void)
    {
      return *Peek<I> ();
    }
    template<class I, class K>
    void Pack (K k)
    {
      details::SwapHelper<I>::Reset (k) (GetAggregated<I> ().mClient);
    }
    template<class C>
    void VisitAndInject (C &c)
    {
      injection::Visitor<InitialArg, Args...>::template Apply (*this, c);
    }
  protected:
    Aggregator()
    {}
  private:
    template<class I>
    Aggregated<I> & GetAggregated (void)
    {
      return GetDerived ();
    }
    template<class I>
    Aggregated<I> const & GetAggregated (void) const
    {
      return GetDerived ();
    }
    Derived & GetDerived (void)
    {
      return static_cast<Derived &> (*this);
    }
    Derived const & GetDerived (void) const
    {
      return static_cast<Derived const &> (*this);
    }
  }; // class Aggregator

  template<class Derived, class InitialArg>
  class Aggregator<Derived, InitialArg> : virtual protected details::AggregatorBase, virtual public Aggregated<InitialArg>
  {
  public:
    typedef Aggregator ThisType;
    template<class XY1, class XY2, class ...XY3>
    struct Rebind { typedef Aggregator<XY1, XY2, XY3...> Type; };
  public:
    virtual ~Aggregator()
    {}
    template<class I>
    std::shared_ptr<I> const Peek (void) const
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    std::shared_ptr<I> Peek (void)
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    std::shared_ptr<I> const & PeekWithRef (void) const
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    std::shared_ptr<I> & PeekWithRef (void)
    {
      return GetAggregated<I> () ();
    }
    template<class I>
    I const & Ref (void) const
    {
      return *Peek<I> ();
    }
    template<class I>
    I & Ref (void)
    {
      return *Peek<I> ();
    }
    template<class I, class K>
    void Pack (K k)
    {
      details::SwapHelper<I>::Reset (k) (GetAggregated<I> ().mClient);
    }
    template<class C>
    void VisitAndInject (C &c)
    {
      injection::Visitor<InitialArg>::template Apply (*this, c);
    }
  protected:
    Aggregator()
    {}
  private:
    template<class I>
    Aggregated<I> & GetAggregated (void)
    {
      return GetDerived ();
    }
    template<class I>
    Aggregated<I> const & GetAggregated (void) const
    {
      return GetDerived ();
    }
    Derived & GetDerived (void)
    {
      return static_cast<Derived &> (*this);
    }
    Derived const & GetDerived (void) const
    {
      return static_cast<Derived const &> (*this);
    }
  }; // class Aggregator
} // namespace smunix


#endif /* AGGREGATOR_HPP__191208 */
